"""
Created on Nov 10, 2009

@author: aamn
"""
import os, sys
from ConfigParser import SafeConfigParser
from datamodels import KnownDM


class Obs(object):
#==============================================================================
# needs validation module.  
#==============================================================================
    """
    another class that loads attributes from a defaults file or from
    a remote file specified by the user. 

    needs methods for validation against a known set of defaults
    
    Includes: http://code.activestate.com/recipes/426406/
    
    removed __getattr*__ functions as they are painful to control.
    """
    KnownDM = KnownDM

    def __init__(self, *args, **kwargs):
        """
        initialize the DM for a datamodel where ever it lives
        """
        self.__parser = SafeConfigParser()
        self.__parser.optionxform = lambda x: x

        if len(args) > 0:
            if kwargs.has_key('default'): default = kwargs.pop('default')
            else: default = 'generic'
            self._datamodel(args[0], defaultmodel=default)
        else: return

    def __getattr__(self, attr):
        if attr not in self.__dict__: return None
        else: return getattr(self, attr)

    def __call__(self, *args):
        """
        DM() returns a dictionary model of the model
        """

        if len(args) == 1:
            self._datamodel(args[0])

        parser = self.__parser
        return dict([
                     (foo, dict([(o, parser.get(foo, o))
                                 for o in parser.options(foo)]))
                     for foo in parser.sections()
                     ])

#    def __repr__ (self):  return self.__str__()

    def __str__(self):
        """
        pretty print the config elements
        """
        p = self.__parser
        result = []
        if len([foo for foo in self.__dict__.values() if type(foo) is Section]) > 0:
            result.append('<Configuration from %s>' % (getattr(self, 'fileName', None) or ''))
            for s in p.sections():
                result.append('[%s]' % s)
                for o in p.options(s):
                    result.append('%s=%s' % (o, p.get(s, o)))
            return '\n'.join(result)
        else: return ''

    def _datamodel(self, datamodel, defaultmodel='empty'):
        """
        wrapper method to correctly point to a valid model
        """
        if os.path.exists(datamodel):
            fileName = os.path.realpath(datamodel)
        elif datamodel not in KnownDM: return self._datamodel(defaultmodel)
        else:
            file = datamodel + '.ini'
            fileName = os.path.join(os.path.split(os.path.abspath(
                                    sys.modules[self.__module__].__file__))[0],
                                    file)
        self.__parser.read(fileName)
        self.fileName = fileName
        self.datamodel = datamodel
        self._appattr()

    def _appattr(self):
        """
        make sections av as attrs
        """
        for section in self.__parser.sections():
            setattr(self, section, Section(section, self.__parser))
            for option in self.__parser.options(section):
                setattr(getattr(self, section), option,
                        self.__parser.get(section, option))
        self.validate_model()

    def add(self, *args):
        """
        add section and optionally option/value to Obsini
        obsini.add('section',['option','value']
        without option, only section added.
        witout value, empty option added (option='')
        """
        if len(args) == 0 or len(args) > 3: raise RuntimeWarning('elements?')
        else: section = args[0]
        if section not in self.__parser.sections():
            self.__parser.add_section(section)
            setattr(self, section, Section(section, self.__parser))
        if len(args) == 2: args = args + tuple([''])
        try:
            option, value = args[1:3]
            self.__parser.set(section, option, value)
            setattr(getattr(self, section), option,
                        self.__parser.get(section, option))
        except:
            pass
        else:
            self.validate_model()

    def get(self, section, option, parsetype):
        """
        allow individual section element lookup with special parsers
        parsetypes are types in the set of int, bool, float, str
        """
        parser = self.__parser
        if parser.has_option(section, option):
            if parsetype is int:
                return parser.getint(section, option)
            elif parsetype is bool:
                return parser.getboolean(section, option)
            elif parsetype is float:
                return parser.getfloat(section, option)
            elif parsetype is str:
                return parser.get(section, option)
            else:
                return parser.get(section, option)
        else:
            return None

    def save(self, outName):
        f = open(outName, 'w')
        self.__parser.write(f)
        f.close()

    def validate_model(self):
        """
        validate against acceptable values of parameters
        """
        pass

#class Section(object):
class Section(object):
    """
    treat config sections as special.
    it would be nicer if i knew how/could subclass the __dict__ 
    with a default missing
    """
    def __init__(self, name, parser):
        self.__dict__['name'] = name
        self.__dict__['parser'] = parser

    def __getattr__(self, attr):
        if attr not in self.__dict__: return None
        else: return getattr(self, attr)

    def __setattr__(self, attr, value):
        self.__dict__[attr] = str(value)
        self.parser.set(self.name, attr, str(value))

    def __call__(self):
        """
        DM.Section() returns a dictionary of the section elements
        """
        parser = self.parser
        return dict([(o, parser.get(self.name, o))
                    for o in parser.options(self.name)])

    def __str__ (self):
        """
        pretty print the config elements
        """
        p = self.parser
        result = []
        result.append('[%s]' % self.name)
        for o in p.options(self.name):
                result.append('%s=%s' % (o, p.get(self.name, o)))
        return '\n'.join(result)
